/**
 *****************************************************************************
 * DESC ：. 
 * Author : Junwei Zhang 
 * CREATEDATE ：2012-8-10 
 * MODIFYLIST ：       	Name 			 Date 			Reason/Contents
 * 				------------------------------------------------------ 
 * 				 	Junwei Zhang     2012-8-10		Created.
 ****************************************************************************
 **/
package com.me.client;

import java.io.File;
import java.io.FilenameFilter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * 插件加载器. <br/>
 * 下午4:39:59 2012-8-10
 * 
 * @Author <a href="mailto:mestudying@sina.com">Junwei Zhang</a><br/>
 **/
public class JTalkPlugInLoader {
	/** 错误消息. */
	private String lastErrorMsg = null;
	private JTalkClientPlugInInterface[] allPlugins = null;
	private JTalkClientFront parent = null;

	public JTalkPlugInLoader(JTalkClientFront parent) {
		this.parent = parent;
	}

	/**
	 * 初始化文件夹下的插件.
	 * 
	 * @param dir
	 *            插件所在的文件夹.
	 */
	public boolean initPlugins(String dir) {
		// TODO Auto-generated method stub
		if(this.allPlugins != null){
			for(JTalkClientPlugInInterface p : this.allPlugins){
				System.out.println("[PLUGIN-LOADER]: Unloading: " + p.getName());
				p.upload();
			}
		}
		if (!this.loadPlugins(dir)) {
			System.out.println("***" + this.lastErrorMsg);
			System.out.println("Can't initialize plugins.Disabled.");
			return false;
		}
		System.out.println("[PLUGIN-LOADER]: Number of loaded plugins:" + allPlugins.length);
		return true;
	}

	/**
	 * 初始化文件夹下的插件.
	 * 
	 * @param dir
	 *            插件所在的文件夹.
	 * @return
	 */
	private boolean loadPlugins(String dir) {
		if (!new File(dir).isDirectory()) {
			this.lastErrorMsg = "Not a directory:" + dir;
			return false;
		}
		String[] plugInFiles = getPluginNames(dir);
		if (plugInFiles == null) {
			this.lastErrorMsg = "No plugin found in " + dir;
			return false;
		}
		JTalkClientPlugInInterface[] tempPlugIns = new JTalkClientPlugInInterface[plugInFiles.length];
		int count = 0;
		try {
			URL[] urlLoadFrom = new URL[plugInFiles.length];
			for (int i = 0; i < plugInFiles.length; i++) {
				urlLoadFrom[i] = new File(dir + plugInFiles[i]).toURI().toURL();
				System.out.println("+ Searching in: " + urlLoadFrom[i].toString());
			}
			URLClassLoader loader = new URLClassLoader(urlLoadFrom);
			@SuppressWarnings("rawtypes")
			Class tempClass = null;
			for (int i = 0; i < plugInFiles.length; i++) {
				try {
					tempClass = Class.forName(
							"com.me.client.plugins.TicTacToe." + plugInFiles[i].replaceAll("\\.class", ""), true,
							loader);
					JTalkClientPlugInInterface tempObj = (JTalkClientPlugInInterface) tempClass.newInstance();
					if (tempObj != null) {
						tempPlugIns[i] = tempObj;
						tempPlugIns[i].setParentObject(new JTalkClientPluginConnector(this.parent));
						System.out.println("+ Init Plugin: " + tempPlugIns[i].getName() + " --> OK.");
						count++;
					} else {
						System.out.println("*** Can't init plugin: " + plugInFiles[i] + ". Disable this plugin.");
					}
				} catch (Exception e) {
					System.out.println("*** Can't init plugin: " + plugInFiles[i] + ". Disable this plugin.");
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			this.allPlugins = null;
			this.lastErrorMsg = "Exception: " + e.toString();
			return false;
		}
		this.allPlugins = new JTalkClientPlugInInterface[count];
		int j = 0;
		for (int i = 0; i < tempPlugIns.length; i++) {
			if (tempPlugIns[i] != null) {
				this.allPlugins[j] = tempPlugIns[i];
				j++;
			}
		}
		return true;
	}

	/**
	 * 获取文件夹下所有插件.
	 * 
	 * @param dir
	 *            插件所在的文件夹.
	 * @return
	 */
	private static String[] getPluginNames(String dir) {
		if (dir == null) {
			dir = "./plugins";
		}
		File f = new File(dir);
		FilenameFilter filter = new FilenameFilter() {
			@Override
			public boolean accept(File dir, String name) {
				return name.endsWith(".class");
			}
		};
		return f.list(filter);
	}

	/**
	 * 获取所有的插件名.
	 * 
	 * @return
	 */
	public String[] getPluginNames() {
		if (this.allPlugins == null || this.allPlugins.length == 0) {
			return null;
		}
		String[] retVal = new String[this.allPlugins.length];
		for (int i = 0; i < retVal.length; i++) {
			retVal[i] = this.allPlugins[i].getName();
		}
		return retVal;
	}

	/**
	 * 根据插件名称获取插件(所有插件的名称不能相同，否则会出现插件不匹配的问题).
	 * 
	 * @param pluginName
	 *            插件名称.
	 * @return
	 */
	public JTalkClientPlugInInterface getPluginByName(String pluginName) {
		JTalkClientPlugInInterface plugin = null;
		for (JTalkClientPlugInInterface p : this.allPlugins) {
			if (pluginName.equals(p.getName())) {
				plugin = p;
			}
		}
		return plugin;
	}

	/**
	 * 显示插件.
	 * 
	 * @param plugin
	 * @param nameType
	 */
	public void showPluginsGUI(String plugin, int nameType) {
		if (nameType == 0) {
			this.getPluginByClassName(plugin).getMainFrame().setVisible(true);
		} else {
			this.getPluginByName(plugin).getMainFrame().setVisible(true);
		}
	}

	/**
	 * 根据插件类名获取插件.
	 * 
	 * @param className
	 *            插件类名.
	 * @return
	 */
	public JTalkClientPlugInInterface getPluginByClassName(String className) {
		JTalkClientPlugInInterface retVal = null;
		for (JTalkClientPlugInInterface p : this.allPlugins) {
			if (p.getClass().getName().equals(className)) {
				retVal = p;
				break;
			}
		}
		return retVal;
	}

	/**
	 * 获取所有插件的类名称.
	 * 
	 * @return
	 */
	public String[] getPluginClassNames() {
		if (this.allPlugins == null || this.allPlugins.length == 0) {
			return null;
		}
		String[] retVal = new String[this.allPlugins.length];
		for (int i = 0; i < retVal.length; i++) {
			retVal[i] = this.allPlugins[i].getClass().getName();
		}
		return retVal;
	}

	/**
	 * 调用插件方法.
	 * 
	 * @param content
	 * @param caller
	 * @return
	 */
	public String invokePluginMethod(String content, String caller) {
		// TODO Auto-generated method stub
		this.lastErrorMsg = null;
		content = content.trim();
		System.out.println("+ Invoking: " + content + ", recieved from: " + caller);
		if (content == null || content.indexOf("::") == -1) {
			this.lastErrorMsg = "Syntax error. Try module::method!";
			return null;
		}
		String[] tempValues = content.split("::");
		if (tempValues.length != 2) {
			this.lastErrorMsg = "Syntax error. Try module::method!";
			return null;
		}
		String plugin = tempValues[0];
		String method = tempValues[1];
		JTalkClientPlugInInterface pluginObj = null;
		if (plugin == null || "".equals(plugin) || method == null || "".equals(method)) {
			this.lastErrorMsg = "Syntax error";
			return null;
		}
		// TODO
		// Check if plugin is available
		boolean hit = false;
		for (JTalkClientPlugInInterface p : this.allPlugins) {
			if (plugin.endsWith(p.getClass().getSimpleName())) {
				hit = true;
				pluginObj = p;
				break;
			}
		}
		if (!hit) {
			this.lastErrorMsg = "Plugin is not available on this client: " + plugin;
			return null;
		}
		try {
			Method[] methods = pluginObj.getCommandMethods();
			method = method.trim();
			if (method.equals("getMethods()")) { // Get all available commands for remote using
				String methodNames = "";
				for (Method m : methods) {
					methodNames += m.toString() + ";;";
				}
				return methodNames;
			} else {
				// Parse and invoke special plug-in method
				System.out.println("+ Using Plugin: " + pluginObj.getName() + " caller: " + caller);
				String methodName = method.substring(0, method.indexOf('(')).trim();
				String[] params = JTalkPlugInLoader.parseParams(method);
				for (Method m : methods) {
					if (methodName.equals(m.getName())) {
						return (String) m.invoke(pluginObj, params);
					}
				}
				this.lastErrorMsg = "Method not declared: " + method;
				return null;
			}
		} catch (Exception e) {
			e.printStackTrace();
			this.lastErrorMsg = "Can't invoke method: " + content + ". " + e.toString();
		}
		this.lastErrorMsg = "Unexpected Error. Can't invoke this method";
		return null;
	}

	/**
	 * 解析方法参数.
	 * 
	 * @param method
	 *            方法名.
	 * @return
	 */
	private static String[] parseParams(String method) {
		String parameters = method.substring(method.indexOf('(') + 1, method.lastIndexOf(')'));
		String[] params = parameters.split("\"[\\s]*,[\\s]*\"");
		if (params == null || params.length < 1 || (params.length == 1 && "".equals(params[0]))) {
			return null;
		} else {
			params[0] = params[0].replaceFirst("\"", "");
			params[params.length - 1] = params[params.length - 1].replaceFirst("\"", "");
		}
		return params;
	}

	/**
	 * 获取最后一条错误消息.
	 * 
	 * @return
	 */
	public String getLastError() {
		return this.lastErrorMsg;
	}

}
